﻿using System;
using System.Web.UI;

using System.Web.UI.HtmlControls;

using Carbon.Web.Forms.Extensions;

namespace Carbon.Web.Forms.Controls
{
    public delegate void PopupGetViewModelDelegate<TViewModel>(Object sender, PopupGetViewModelEventArgs<TViewModel> args);
    public delegate void PopupViewModelChangedDelegate<TViewModel>(Object sender, PopupViewModelChangedEventArgs<TViewModel> args);
    public delegate void PopupClosedDelegate(Object sender, PopupClosedEventArgs args);

    public enum TransferDataStyle { Client = 0, PersistanceService = 1 }

    public class PopupOpener<TViewModel> : Control, INamingContainer, IPostBackDataHandler, IPostBackEventHandler, ICallbackEventHandler, IScriptControl
    {
        public PopupOpener()
        {
        }

        private class PopupOpenerProperties
        {
            private bool m_OpenPopup = default(bool);
            public bool OpenPopup
            {
                get
                {
                    return m_OpenPopup;
                }
                set
                {
                    m_OpenPopup = value;
                }
            }

            private String m_SerializedData = default(String);
            public String SerializedData
            {
                get
                {
                    return m_SerializedData;
                }
                set
                {
                    m_SerializedData = value;
                }
            } 
        }

		#region Classes (1)
		private class PopupOpenerScriptDescriptor<T> : ScriptDescriptor
        {
			#region Fields (1)
			private PopupOpener<TViewModel> m_Parent = default(PopupOpener<TViewModel>);
			#endregion

			#region Properties (1)
			public PopupOpener<TViewModel> Parent
            {
                get
                {
                    return m_Parent;
                }
            }
			#endregion

			#region Constructors (1)
			public PopupOpenerScriptDescriptor(PopupOpener<TViewModel> parent)
                : base()
            {
                if (parent == null) throw new ArgumentNullException("parent");

                this.m_Parent = parent;
            }
			#endregion

			#region Methods (1)

			protected override string GetScript()
            {
                return string.Format("$find('{0}').InitializePopup('{0}', '{1}', '{2}', '{3}', {4}, {5}, {6}, {7}, {8}, {9});",
                    Parent.ClientID,
                    Parent.UniqueID,
                    Parent.Control_SerializedDataControl.ClientID,
                    Parent.Url,
                    Parent.Width,
                    Parent.Height,
                    Parent.UseAsync.ToString().ToLowerInvariant(),
                    Parent.FetchFromParent.ToString().ToLowerInvariant(),
                    Parent.OpenPopup.ToString().ToLowerInvariant(),
                    ((int)Parent.TransferStyle).ToString()
                    );
            }

			#endregion
        }
		#endregion

		#region Fields (9)
		private TransferDataStyle m_TransferStyle = TransferDataStyle.Client;

		private bool m_FetchFromParent = true;
		private bool m_OpenPopup = default(bool);
		private bool m_UseAsync = true;

		private int m_Height = 400;
		private int m_Width = 600;

		private String m_Url = default(String);

		protected HtmlInputHidden ctrl_SerializedData;
		#endregion

		#region Events (3)
		public event PopupGetViewModelDelegate<TViewModel> GetViewModel;

		public event PopupClosedDelegate PopupClosed;

		public event PopupViewModelChangedDelegate<TViewModel> ViewModelChanged;
		#endregion

		#region Properties (9)
		protected HtmlInputHidden Control_SerializedDataControl
        {
            get
            {
                if (ctrl_SerializedData == null) throw new NullReferenceException("ctrl_SerializedData");

                return ctrl_SerializedData;
            }
        }
		public bool FetchFromParent
        {
            get
            {
                return m_FetchFromParent;
            }
            set
            {
                m_FetchFromParent = value;
            }
        }
		public int Height
        {
            get
            {
                return m_Height;
            }
            set
            {
                m_Height = value;
            }
        }
		private bool OpenPopup
        {
            get
            {
                return m_OpenPopup;
            }
            set
            {
                m_OpenPopup = value;
            }
        }

        private String m_SerializedData = null;
		private String SerializedData
        {
            get
            {
                return m_SerializedData;
            }
            set
            {
                m_SerializedData = value;
            }
        }
		public TransferDataStyle TransferStyle
        {
            get
            {
                return m_TransferStyle;

            }
            set
            {
                m_TransferStyle = value;
            }
        }
		public String Url
        {
            get
            {

                if (string.IsNullOrEmpty(m_Url)) throw new ArgumentOutOfRangeException("m_Url");
                return m_Url;
            }
            set
            {
                m_Url = value;
            }
        }
		public bool UseAsync
        {
            get
            {
                return m_UseAsync;
            }
            set
            {
                m_UseAsync = value;
            }
        }
		public int Width
        {
            get
            {
                return m_Width;
            }
            set
            {
                m_Width = value;
            }
        }
		#endregion

		#region ASP Processing

		protected override void OnInit(EventArgs e)
        {
            base.OnInit(e);
            EnsureChildControls();

            ctrl_SerializedData = new HtmlInputHidden();
            ctrl_SerializedData.ID = "getdata";
            ctrl_SerializedData.EnableViewState = false;
            this.Controls.Add(ctrl_SerializedData);
        }

		public bool LoadPostData(string postDataKey, System.Collections.Specialized.NameValueCollection postCollection)
        {
            return false;
        }

		public virtual void RaisePostBackEvent(string eventArgument)
        {
            if (eventArgument == "ViewModelChanged")
            {
                OnPopupClosed(ctrl_SerializedData.Value);
            }
            else if (eventArgument == "Fetch")
            {
                TViewModel data = GetPopupViewModelSync();

                SerializedData = GetSerializedObject(data);
                OpenPopup = true;
            }
        }

		public void RaisePostDataChangedEvent()
        {

        }

		protected override void OnPreRender(EventArgs e)
        {
            base.OnPreRender(e);

            this.Page.RegisterRequiresPostBack(this);
            this.Page.RegisterRequiresRaiseEvent(this);

            if (!UseAsync && FetchFromParent)
            {
                TViewModel data = GetPopupViewModelSync();

                this.Control_SerializedDataControl.Value = GetSerializedObject(data);
            }

            if (UseAsync)
            {
                if (OpenPopup)
                    this.Control_SerializedDataControl.Value = SerializedData;
                else
                    this.Control_SerializedDataControl.Value = string.Empty;
            }

            ScriptManager sm = ScriptManager.GetCurrent(this.Page);

            if (sm == null) throw new NullReferenceException("sm");

            sm.RegisterScriptControl(this);
            sm.RegisterScriptDescriptors(this);
        }

		public override void RenderControl(HtmlTextWriter writer)
        {
            if (!this.Visible)
            {
                return;
            }

            writer.AddAttribute(HtmlTextWriterAttribute.Id, this.ClientID);
            writer.AddStyleAttribute(HtmlTextWriterStyle.Display, "none");
            writer.RenderBeginTag(HtmlTextWriterTag.Div);
            {
                if (Control_SerializedDataControl.ID == null) throw new NullReferenceException("Control_SerializedDataControl.ID");
                Control_SerializedDataControl.RenderControl(writer);
            }
            writer.RenderEndTag();
        }

		#endregion

		#region Methods (10)

		public string GetCallbackResult()
        {
            throw new NotImplementedException();
        }

		public TViewModel GetDeserializedObject(String value)
        {
            if (TransferStyle == TransferDataStyle.Client)
            {
                return InternalUtility.CompactDeserialize<TViewModel>(value);
            }
            else if (TransferStyle == TransferDataStyle.PersistanceService)
            {
                string[] values = value.Split(',');
                String identifier = values[0].Trim();
                Guid key = new Guid(values[1].Trim());
                IPersistanceService service = FormsContext.Current.PersistanceService;
                byte[] data = service.LoadBinaryData(identifier, key);
                
                service.FreeIdentifier(identifier, key);

                return InternalUtility.CompactDeserializeFromByteArray<TViewModel>(data);
            }
            else
            {
                throw new ArgumentOutOfRangeException("TransferStyle");
            }
        }

		public String GetOpenPopupJavascript()
        {
            return string.Format("$find('{0}').OpenPopup();", this.ClientID);
        }

		private TViewModel GetPopupViewModelSync()
        {
            if (GetViewModel == null)
                throw new InvalidOperationException("Must specify how to fetch data by subscribing to 'GetPopupViewModel' event.");

            PopupGetViewModelEventArgs<TViewModel> args = new PopupGetViewModelEventArgs<TViewModel>();
            GetViewModel(this, args);

            if (args.ViewModel == null)
                throw new InvalidOperationException("Fetched ViewModel can't be null.");

            TViewModel data = args.ViewModel;

            if (data == null)
                throw new InvalidOperationException("Fetched data is null.");
            return data;
        }

		public System.Collections.Generic.IEnumerable<ScriptDescriptor> GetScriptDescriptors()
        {
            ScriptComponentDescriptor descriptor = new ScriptComponentDescriptor("Carbon.Popup") { ID = ClientID };
            PopupOpenerScriptDescriptor<TViewModel> desc2 = new PopupOpenerScriptDescriptor<TViewModel>(this);

            return new ScriptDescriptor[] { descriptor, desc2 };
        }

		public System.Collections.Generic.IEnumerable<ScriptReference> GetScriptReferences()
        {
            return EmptyArray<ScriptReference>.Instance;
        }

		private String GetSerializedObject(TViewModel value)
        {
            if (TransferStyle == TransferDataStyle.Client)
            {
                return InternalUtility.CompactSerialize<TViewModel>(value);
            }
            else if (TransferStyle == TransferDataStyle.PersistanceService)
            {
                byte[] data = InternalUtility.CompactSerializeToByteArray(value);

                IPersistanceService service = FormsContext.Current.PersistanceService;
                Guid key = Guid.NewGuid();
                Object identifier = service.StoreBinaryData(key, data);

                return ConvertInvariant.ToString(identifier) + "," + key;
            }
            else
            {
                throw new ArgumentOutOfRangeException("TransferStyle");
            }
        }

		protected virtual void OnPopupClosed(String value)
        {
            if (PopupClosed != null)
            {
                PopupClosed(this, new PopupClosedEventArgs(value));
            }
            else
            {
                TViewModel mvalue = GetDeserializedObject(value);
                OnPopupViewModelChanged(new PopupViewModelChangedEventArgs<TViewModel>(mvalue));
            }
        }

		protected virtual void OnPopupViewModelChanged(PopupViewModelChangedEventArgs<TViewModel> args)
        {
            if (ViewModelChanged != null)
                ViewModelChanged(this, args);
        }

		public void RaiseCallbackEvent(string eventArgument)
        {

        }

		#endregion
    }
}